﻿@using System.Runtime.InteropServices.Marshalling
@using HardHatCore.HardHatC2Client.Models
@using HardHatCore.HardHatC2Client.Pages
@using System.Collections.Concurrent
@using System.Collections.ObjectModel
@using HardHatCore.ApiModels.Shared.TaskResultTypes;
@using HardHatCore.HardHatC2Client.Plugin_BaseClasses;
@using HardHatCore.HardHatC2Client.Plugin_Interfaces;
@using HardHatCore.HardHatC2Client.Utilities
@inject IToastService toastDefault
 <MudDynamicTabs @ref="@DynamicTabs" Elevation="4" PanelClass="px-4 py-6" Rounded="true" ApplyEffectsToContainer="true" CloseTab="@((panel) => CloseTabCallback(panel))" @bind-ActivePanelIndex="UserIndex">
     @foreach(var tab in Tabs)
     {
         <MudTabPanel Tag="@tab.TabId" Text="@tab.Name" Icon="@Icons.Material.Outlined.DesktopWindows">
             <!-- a dropdown menu of all the engineers on the host who can list content  -->
             <div class="d-flex">
             @*<MudSwitch @bind-Checked="@HideOfflineImplants" Label="Hide Offline Implants" Color="Color.Primary" />*@
             <MudToggleIconButton @bind-Toggled="@HideOfflineImplants" Color="Color.Primary" Icon="@Icons.Material.Filled.Visibility" ToggledIcon="@Icons.Material.Filled.VisibilityOff" Title="Hide Offline Implants"  ToggledTitle="Hide Offline Implants" />
             <MudToggleIconButton @bind-Toggled="@HostOnlyImplants" Color="Color.Primary" Icon="@Icons.Material.Filled.FilterAlt" ToggledColor="@Color.Error" ToggledIcon="@Icons.Material.Filled.FilterAltOff" Title="Host Only Implants"  ToggledTitle="Host Only Implants" />
             @*<MudSwitch @bind-Checked="@HostOnlyImplants" Label="Host Only Implants" Color="Color.Primary" />*@
             </div>
             <div class="d-flex mb-xl-2">
                <MudSelect Class="mb-4 mt-4" T="ExtImplant_Base" Label="Implant To Browse With" Variant="Variant.Filled" Required="true" @bind-value="selectedImplant" RequiredError="Implant is required!">
                     @if (HostOnlyImplants)
                     {
                         if (HideOfflineImplants)
                         {
                            foreach (ExtImplant_Base imp in Implants.ImplantList)
                             {
                                 if (imp.Status == null)
                                 {
                                     Implants.GetImplantStatus(imp);
                                 }
                             }
                            foreach (ExtImplant_Base imp in Implants.ImplantList.Where(x => x.Metadata.Hostname.Equals(tab.Name, StringComparison.CurrentCultureIgnoreCase) && x.Status.Equals("Active", StringComparison.CurrentCultureIgnoreCase)))
                             {
                                 <MudSelectItem Value="@(imp)">
                                    <MudText>@imp.Metadata.Username - @imp.Metadata.ProcessId@@@imp.Metadata.Address, Int: @imp.Metadata.Integrity, Sleep: @imp.Metadata.Sleep </MudText>
                                 </MudSelectItem>
                             }
                         }
                         else
                         {
                            foreach (ExtImplant_Base imp in Implants.ImplantList.Where(x => x.Metadata.Hostname.Equals(tab.Name, StringComparison.CurrentCultureIgnoreCase)))
                             {
                                 <MudSelectItem Value="@(imp)">
                                    <MudText>@imp.Metadata.Username - @imp.Metadata.ProcessId@@@imp.Metadata.Address, Int: @imp.Metadata.Integrity, Sleep: @imp.Metadata.Sleep </MudText>
                                 </MudSelectItem>
                             }
                         }
                     }
                     else
                     {
                         if (HideOfflineImplants)
                         {
                            foreach (ExtImplant_Base imp in Implants.ImplantList.Where(x => x.Status.Equals("Active", StringComparison.CurrentCultureIgnoreCase)))
                             {
                                 <MudSelectItem Value="@(imp)">
                                    <MudText>@imp.Metadata.Username - @imp.Metadata.ProcessId@@@imp.Metadata.Address, Int: @imp.Metadata.Integrity, Sleep: @imp.Metadata.Sleep </MudText>
                                 </MudSelectItem>
                             }
                         }
                         else
                         {
                            foreach (ExtImplant_Base imp in Implants.ImplantList)
                             {
                                 <MudSelectItem Value="@(imp)">
                                    <MudText>@imp.Metadata.Username - @imp.Metadata.ProcessId@@@imp.Metadata.Address, Int: @imp..MetadataIntegrity, Sleep: @imp.Metadata.Sleep </MudText>
                                 </MudSelectItem>
                             }
                         }

                     }
                 </MudSelect>
                @* <MudTextField Class="mb-4 mt-4" @ref=pathTextFiled TextChanged="OnPathChanged" @bind-Value="targetPath" Label="Path To Browse" Variant="Variant.Filled" />*@
                 <MudCheckBox @bind-Checked="@GetACLs" Label="Get ACLs?" Color="Color.Primary"></MudCheckBox>
                 <MudCheckBox @bind-Checked="@GetItemCount" Label="Get Item Count?" Color="Color.Primary"></MudCheckBox>
            </div>
             <div class="d-flex mb-xl-2">
                 <MudCard Class="" Style="overflow-y:auto" Outlined="true">
                     <MudCardContent Style="height:900px; width:450px">
                         <MudTreeView @bind-SelectedValue="SelectedValue" Hover="true" Dense="true" Color="Color.Primary" T="TreeItemData" Items="@tab.Content">
                             <ItemTemplate>
                                 <MudTreeViewItem Value="@context" @bind-Expanded="@context.IsExpanded" Items="@context.ChildTreeItems">
                                     <Content>
                                         <MudTreeViewItemToggleButton @bind-Expanded="@context.IsExpanded" Visible="@context.HasChild"/>
                                         @if (context.ItemCount > 0)
                                         {
                                             <MudBadge Color="Color.Success" Overlap="true" Bordered="true" Class="mx-6 my-4" Content="context.ItemCount">
                                                 <MudIcon Icon=@context.Icon Class="ml-0 mr-2" Color=@context.IconColor />
                                             </MudBadge>
                                         }
                                         else if (context.ItemCount == -1)
                                         {
                                             <MudBadge Icon="@Icons.Material.Filled.Lock" Color="Color.Error" Overlap="true" Bordered="true" Class="mx-6 my-4">
                                                 <MudIcon Icon=@context.Icon Class="ml-0 mr-2" Color=@context.IconColor />
                                             </MudBadge>
                                         }
                                         else if (context.ItemCount == 0)
                                         {
                                             <MudBadge Color="Color.Error" Overlap="true" Bordered="true" Class="mx-6 my-4" Content="context.ItemCount">
                                                 <MudIcon Icon=@context.Icon Class="ml-0 mr-2" Color=@context.IconColor />
                                             </MudBadge>
                                         }
                                         else
                                         {
                                             <MudIcon Icon=@context.Icon Class="ml-0 mr-2" Color=@context.IconColor/>
                                         }

                                         <MudText>@context.Text</MudText>
                                     </Content>
                                 </MudTreeViewItem>
                             </ItemTemplate>
                         </MudTreeView>
                     </MudCardContent>
                 </MudCard>
                 <MudCard Class="" Style="overflow-y:auto; Width:100%" Outlined="true">
                     <MudCardContent Style="height:900px; Width:100%">

                         @if (SelectedValue != null)
                         {
                             string path = "";
                             List<FileSystemItem> outputs = new List<FileSystemItem>();
                            <!-- check the DirectroyContent for keys matching the SelectedValue.fullPath ignoring trailing \, spaces, or captalization -->
                            foreach (string key in tab.DirectoryContent.Keys)
                            {
                                //remove leading and trailing spaces from SelectedValue.FullPath and from the key
                                SelectedValue.FullPath = SelectedValue.FullPath.Trim();
                                string keyTrimmed = key.Trim();
                                //remove trailing \ from SelectedValue.FullPath and from the key
                                SelectedValue.FullPath = SelectedValue.FullPath.TrimEnd('\\');
                                keyTrimmed = keyTrimmed.TrimEnd('\\');
                                //if the key matches the SelectedValue.FullPath then add the value to the outputs list
                                if (keyTrimmed.Equals(SelectedValue.FullPath, StringComparison.OrdinalIgnoreCase))
                                {
                                    outputs.AddRange(tab.DirectoryContent[key]);
                                    path = key;
                                    SelectedValue.Icon = Icons.Material.Filled.FolderOpen;
                                    SelectedValue.IconColor = Color.Primary;
                                    break;
                                }
                            }
                            if (!String.IsNullOrWhiteSpace(path))
                            {
                                if (LocationsToBrowse.Contains(SelectedValue.FullPath.Trim()))
                                {
                                    LocationsToBrowse.Remove(SelectedValue.FullPath.Trim());
                                    ShowSuccessToast($"Got back content for {SelectedValue.FullPath}");
                                   // OnStateChange();
                                }
                                 <DirectoryListingTable IsFileBrowser="true" Outputs="@outputs" Path="@SelectedValue.FullPath"></DirectoryListingTable>
                             }
                             else
                             {
                                 <MudProgressCircular Color="Color.Primary" Indeterminate="true"/>
                                 if (!LocationsToBrowse.Contains(SelectedValue.FullPath.Trim()) && selectedImplant != null)
                                 {
                                     LocationsToBrowse.Add(SelectedValue.FullPath.Trim());
                                     GetFiles(selectedImplant, SelectedValue.FullPath.Trim());
                                 }
                             }
                         }
                         else
                         {
                             <MudAlert Severity="Severity.Error">No Directory Selected</MudAlert>
                         }
                     </MudCardContent>
                 </MudCard>
             </div>

         </MudTabPanel>
     }
 </MudDynamicTabs>

@code {
    [Inject]
    private static IToastService toastService { get; set; }

    //public static List<string> InputsToParse = new List<string>();
    //the first string is the hostname that the file listing hashset is associated with
    public static List<TabView> Tabs = new List<TabView>();
    public static List<TabView> backupTabs = new List<TabView>();
    public MudDynamicTabs? DynamicTabs;
    public static int UserIndex = 0;
    private bool _updateIndex = false;
    private static TreeItemData SelectedValue { get; set; }
    public static ExtImplant_Base selectedImplant = null;
    private static ObservableCollection<string> LocationsToBrowse = new ObservableCollection<string>();
    public static string? targetPath = null;
    public static bool GetItemCount = false;
    public static bool GetACLs = false;
    public bool HostOnlyImplants = true;
    public bool HideOfflineImplants = false;
    //  public delegate void OnStateChangeDelegate();
    //  public static OnStateChangeDelegate OnStateChange;
    public static MudTextField<string> pathTextFiled; 

    public class TabView
    {
        public string Name { get; set; }
        public HashSet<TreeItemData> Content { get; set; }
        public string TabId { get; set; }
        public Dictionary<string, List<FileSystemItem>> DirectoryContent = new();
    }    


    public class TreeItemData
    {
        public string Text { get; set; }

        public string FullPath { get; set; }

        public string? Icon { get; set; } = Icons.Material.Filled.Folder;

        public Color IconColor { get; set; } = Color.Default;

        public bool IsExpanded { get; set; } = false;

        public bool HasChild => ChildTreeItems != null && ChildTreeItems.Count > 0;

        public int ItemCount { get; set; }

        public HashSet<TreeItemData> ChildTreeItems { get; set; } = new HashSet<TreeItemData>();

        public TreeItemData(string text,string fullPath)
        {
            Text = text;
            FullPath = fullPath;
        }
    }


    public static async Task AddSideBarContent(string hostname, List<FileSystemItem> inputs, TabView tabview)
    {
        //remove any items from inputsToParse that contain a . in the path
        List<FileSystemItem> inputsToParse = inputs.Where(x => !x.Name.Contains('.')).ToList();

        //get the tabs current side bar content
        HashSet<TreeItemData> treeItems = tabview.Content;


        //split the elements in InputsToParse at the first | then take that first element which is a path and if it ends in a .* then it is a file otherwise it is a folder , after that build a hierarchy of folders and files where each \ is a higher tier
        foreach(FileSystemItem input in inputsToParse)
        {
            if (String.IsNullOrWhiteSpace(input.Name))
            {
                continue;
            }
            //this is the parts of the path split by \, for example c:\ Windows\ System32\ 
            string[] splitPath = input.Name.Split('\\');
            //foreach element in the split path if it already exists in the hashset then skip it and add the next element to its child tree items
            //if it does not exist then add it to the hashset and add the next element to its child tree items
            for(int i=0; i<splitPath.Length; i++)
            {
                string path = splitPath[i];

                //if the path ends in .* then it is a file otherwise it is a folder
                if (path.Contains("."))
                {
                    continue;
                }

                //if i is larger then 0 then add this element to the child tree items of the previous element
                if (i > 0)
                {
                    //full path should be the previous element's full path + \ + this element
                    string fullPath = "";

                    //get the previous element
                    string previousPath = splitPath[i - 1];
                    //get the tree item that has the previous path
                    TreeItemData? treeItem = treeItems.FirstOrDefault(x => x.Text == previousPath);
                    //if the treeItem is null then go up a level and serach for the previous path in that childTreeItems
                    if (treeItem == null)
                    {
                        //foreach element in the hashset check if it has the previous path in its child tree items if it does then set the tree item to that 
                        HashSet<TreeItemData> TempItems = new HashSet<TreeItemData>();
                        foreach (TreeItemData item in treeItems)
                        {
                            TempItems.Add(item);
                        }
                        //while treeItem is null call ParseNestedDirectoryData passing in the hashset and the previous path
                        while (treeItem == null)
                        {
                            //if the hashset is empty then break out of the loop
                            if (TempItems.Count == 0)
                            {
                                break;
                            }
                            treeItem = ParseNestedDirectoryData(TempItems, previousPath);
                            //set the hashset to the child tree items of each element in the hashset
                            HashSet<TreeItemData> TempItems2 = new HashSet<TreeItemData>();
                            foreach (TreeItemData item in TempItems)
                            {
                                foreach (TreeItemData childItem in item.ChildTreeItems)
                                {
                                    TempItems2.Add(childItem);
                                }
                            }
                            TempItems = TempItems2;
                        }
                    }

                    //if treeItem is still null then skip this element
                    if (treeItem == null)
                    {
                        continue;
                    }

                    //if treeItem already contains the path then skip it
                    if (treeItem.ChildTreeItems.Any(x => x.Text.Equals(path,StringComparison.CurrentCultureIgnoreCase)))
                    {
                        continue;
                    }

                    //add the path to the child tree items of the previous element
                    if (path.Contains("."))
                    {
                        continue;
                    }
                    else
                    {
                        fullPath = treeItem.FullPath + "\\" + path;
                        TreeItemData childTreeItem = new TreeItemData(path, fullPath);
                        if (fullPath.Equals(input.Name.TrimEnd('\\'), StringComparison.CurrentCultureIgnoreCase))
                        {
                            childTreeItem.ItemCount = (int)input.ChildItemCount;
                        }
                        treeItem.ChildTreeItems.Add(childTreeItem);
                    }
                    continue;
                }



                //if the hashset does not contain the path then add it
                if (treeItems.Any(x => x.Text.Equals(path, StringComparison.CurrentCultureIgnoreCase))) 
                {
                    continue;
                }

                //get the tree Item that holds the hostname and add the path to its child tree items
                TreeItemData treeItemData = treeItems.FirstOrDefault(x => x.Text == hostname);
                //check if the path is already in the child tree items of the hostname
                if (treeItemData.ChildTreeItems.Any(x => x.Text == path))
                {
                    continue;
                }
                TreeItemData otherchildItem = new TreeItemData(path, path);
                otherchildItem.ItemCount = (int)input.ChildItemCount;
                treeItemData.ChildTreeItems.Add(otherchildItem);


            }
        }
    }

    //add a function that takes in a engineer object and a path string and issues a command to the engineer to get the files in that path
    private static async Task GetFiles(ExtImplant_Base implant, string path)
    {
        if(implant == null)
        {
            return;
        }
        string command = $"ls /path {path}";
        if (GetACLs)
        {
            command += " /getacls true";
        }
        if (GetItemCount)
        {
            command += " /getcount true";
        }
        ShowInfoToast($"getting content of {path}");
        var testInt = new InteractCommandEntryWindow();
        await testInt.SendTask(implant, command);
    }


    //make a function to handle the MudTextField ValueChanged event
    public static async Task OnPathChanged(string s)
    {
        if(selectedImplant == null)
        {
            return;
        }
        if(targetPath == null)
        {
            return;
        }
        string path = targetPath;
        path = path.Trim();
        string command = "ls /path " + path;
        await GetFiles(selectedImplant, path);
        targetPath = null;
    }

    public static void ShowSuccessToast(string successMessage)
    {
        toastService.ShowSuccess(successMessage);
    }
    public static void ShowErrorToast(string errorMessage)
    {
        toastService.ShowError(errorMessage);
    }

    public static void ShowInfoToast(string infoMessage)
    {
        toastService.ShowInfo(infoMessage);
    }

    public static void ShowWarningToast(string warningmessage)
    {
        toastService.ShowWarning(warningmessage);
    }

    //returns the tree item that has the previous path in its child tree items
    private static TreeItemData ParseNestedDirectoryData(HashSet<TreeItemData> TempItems,string previousPath)
    {
        TreeItemData? treeItem = null;
        foreach (TreeItemData item in TempItems)
        {
            if (item.ChildTreeItems.Any(x => x.Text.Equals(previousPath,StringComparison.CurrentCultureIgnoreCase)))
            {
                treeItem = item.ChildTreeItems.FirstOrDefault(x => x.Text.Equals(previousPath,StringComparison.CurrentCultureIgnoreCase));
                break;
            }
        }
        //return the tree item or null if it does not exist
        return treeItem ?? null;
    }

    private void CloseTabCallback(MudTabPanel panel)
    {
        var tabView = Tabs.FirstOrDefault(x => x.TabId == (string)panel.Tag);
        if (tabView != null)
        {
            Tabs.Remove(tabView);
            _updateIndex = true;
        }
    }


    public static async Task AddContent(string hostname,string path, List<FileSystemItem> inputs)
    {
        try
        {
            path = path.TrimEnd('\\');
            //get the tab that has the hostname, or set it to null if it does not exist
            TabView? tabView = null;
            tabView = Tabs.FirstOrDefault(x => x.Name.Equals(hostname, StringComparison.CurrentCultureIgnoreCase), defaultValue:null); 
            //if its still null create a new tabView object and assign some defaults 
            if(tabView is null)
            {
                //create a new tab with the hostname as the name and the hashset as the content
                tabView = new TabView() { Name = hostname, Content = new HashSet<TreeItemData>(), DirectoryContent = new Dictionary<string, List<FileSystemItem>>(), TabId = Guid.NewGuid().ToString() };
                Tabs.Add(tabView);
                //get the tab that has the hostname
                backupTabs.Add(tabView);
                //add the hostname as the first item to the hashset
                TreeItemData host = new TreeItemData(hostname,hostname);
                host.Icon = Icons.Material.Outlined.DesktopWindows;
                host.IconColor = Color.Info;
                tabView.Content.Add(host);
            }

            //if DirectoryContent does not already contain the path then add it and the inputs, otherwise update the list with missing entries
            if (!tabView.DirectoryContent.ContainsKey(path))
            {
                await AddSideBarContent(hostname, inputs, tabView);
                tabView.DirectoryContent.Add(path, inputs);
                //get the TreeItemData with the name of the path and set its icon color to primary if inputs.Count >0 
                TreeItemData? targetItem = null;
                HashSet<TreeItemData> TempItems = new HashSet<TreeItemData>();
                TempItems = tabView.Content;
                path = path.TrimEnd('\\');
                path = path.Trim();
                while (targetItem == null)
                {
                    if (TempItems.Count == 0)
                    {
                        break;
                    }
                    foreach (TreeItemData item in TempItems)
                    {
                        if (item.ChildTreeItems.Any(x => x.FullPath.Equals(path, StringComparison.CurrentCultureIgnoreCase)))
                        {
                            targetItem = item.ChildTreeItems.FirstOrDefault(x => x.FullPath.Equals(path,StringComparison.CurrentCultureIgnoreCase));
                            break;
                        }
                    }
                    if (targetItem != null)
                    {
                        break;
                    }
                    HashSet<TreeItemData> TempItems2 = new HashSet<TreeItemData>();
                    foreach (TreeItemData item in TempItems)
                    {
                        foreach (TreeItemData childItem in item.ChildTreeItems)
                        {
                            TempItems2.Add(childItem);
                        }
                    }
                    TempItems = TempItems2;
                }
                if (targetItem != null)
                {
                    if (inputs.Count > 0)
                    {
                        targetItem.IconColor = Color.Primary;
                    }
                }
            }
            else
            {
                //get the list of inputs for the path
                List<FileSystemItem> existingInputs = tabView.DirectoryContent[path];
                //remove all the items in inputs that exist in the existingInputs list
                List<FileSystemItem> newResults = inputs.Except(existingInputs).ToList();

                if (newResults.Count > 0)
                {
                    //add the remaining items to the existingInputs list
                    existingInputs.AddRange(newResults);
                    //populate the sidebar with these new inputs
                    await AddSideBarContent(hostname, newResults, tabView);
                }
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
            Console.WriteLine(e.StackTrace);
        }
    }


    // public void ImplementOnStateChangeEvent()
    // {
    //     InvokeAsync(StateHasChanged);
    // }

    protected override async Task OnInitializedAsync()
    {

       // OnStateChange = ImplementOnStateChangeEvent;
        toastService = toastDefault;
    }

}